#include "FbxMesh.h"

namespace GC
{
	GC::FbxMesh::FbxMesh(const FbxNode* a_meshNode, fbxsdk_2014_1::FbxMesh* a_mesh,const FbxNode* a_rootNode )
		: m_name( a_meshNode->GetName() ),
		m_id( FbxHelper::GetNodeID( a_meshNode ) ),
		m_verticeCount( a_mesh->GetControlPointsCount() ),
		m_facesCount( a_mesh->GetPolygonCount() ),
		m_verticePerFace(3   ),
		m_uvChannelsCount( a_mesh->GetElementUVCount() )
	{		
		m_initialized = Initialize( a_mesh ) && SetValues( a_meshNode, a_mesh, a_rootNode );		
	}	

	const bool GC::FbxMesh::IsInitialized()
	{
		return m_initialized;
	}

	/* GET VARIABLES FOR EXPORTERS */
	const FbxString GC::FbxMesh::GetName() const
	{
		return m_name;
	}
	/// <summary>
	/// Gets the unique ID based off FbxHelper::GetID( FbxNode* );
	/// </summary>
	/// <returns></returns>
	const int GC::FbxMesh::GetID() const
	{
		return m_id;
	}
	
	const int GC::FbxMesh::GetFacesCount() const
	{
		return m_facesCount;
	}
	const int GC::FbxMesh::GetVerticesPerFace() const
	{
		return m_verticePerFace;
	}
	const size_t GC::FbxMesh::GetVerticesSize() const
	{
		return m_vertices.size();
	}
	/// <summary>
	/// Gets the size of m_verticeIndex. 
	/// It's facesCount * verticesPerFace
	/// </summary>
	/// <returns></returns>
	const size_t GC::FbxMesh::GetVerticesIndexSize() const
	{
		return m_verticeIndex.size();		
	}
	const size_t GC::FbxMesh::GetNormalsSize() const
	{
		return m_normals.size();
	}
	const size_t GC::FbxMesh::GetUvChannelsSize() const
	{
		return m_uvCoordinates.size();
	}
	const size_t GC::FbxMesh::GetBonesSize() const
	{
		return m_bones.size();
	}
	

	const VectorConstDouble GC::FbxMesh::GetTransform() const
	{
		return m_transform;
	}
	const VectorConstDouble GC::FbxMesh::GetVertices() const
	{
		return m_vertices;
	}	
	const VectorConstInt GC::FbxMesh::GetVerticesIndex() const
	{
		return m_verticeIndex;
	}
	const VectorConstDouble GC::FbxMesh::GetNormals() const
	{
		return m_normals;
	}

	const Vector2DConstDouble GC::FbxMesh::GetUVCoords() const
	{
		return m_uvCoordinates;
	}
	const GC::VectorFbxBonePtr GC::FbxMesh::GetBones() const
	{
		return m_bones;
	}


	/* Private functions */

	const bool GC::FbxMesh::Initialize( fbxsdk_2014_1::FbxMesh* a_mesh )
	{
		m_transform = FbxHelper::GetDQorMatrix4x4();
		
		m_vertices.reserve(m_verticeCount * m_verticePerFace);
		
		m_verticeIndex.reserve(m_facesCount * m_verticePerFace);

		m_normals.reserve( m_facesCount * m_verticePerFace);

		//Init UV Channels
		if (m_uvChannelsCount > 0)
		{
			//Initiate the uv channels array
			m_uvCoordinates.resize(m_uvChannelsCount, VectorConstDouble());
			const int f_uvCoordsCount = m_facesCount * m_verticePerFace * 2;

			for (int i = 0; i < m_uvChannelsCount; ++i)
			{
				m_uvCoordinates[i].reserve(f_uvCoordsCount);
			}
		}
		//Set bonecount incase 0 is not default value
		m_boneCount = 0;
		//Gets the amount of deformers affecting the model, 1 deformer is one bone basicly
		int f_deformerCount = a_mesh->GetDeformerCount(FbxDeformer::eSkin);
		
		if (f_deformerCount > 0)
		{
			for (int i = 0; i < f_deformerCount; ++i)
			{
				m_boneCount += ((FbxSkin *)a_mesh->GetDeformer(i, FbxDeformer::eSkin))->GetClusterCount();
			}

			m_bones.reserve( m_boneCount);
		}

		return true;
	}

	/// <summary>
	/// Sets the meshes variable values, Sets the vertices, faces, normals e.t.c.
	/// </summary>
	/// <param name="a_meshNode">The FBX SDK Node</param>
	/// <param name="a_mesh">The FBX SDK Mesh</param>
	/// <param name="a_rootNode">The FBX SDK scene root node.</param>	
	const bool GC::FbxMesh::SetValues(const FbxNode* a_meshNode, fbxsdk_2014_1::FbxMesh* a_mesh, const FbxNode* a_rootNode)
	{
		fbxsdk_2014_1::FbxVector4 f_translation = fbxsdk_2014_1::FbxVector4(a_meshNode->LclTranslation.Get());
		fbxsdk_2014_1::FbxVector4 f_rotation = fbxsdk_2014_1::FbxVector4(a_meshNode->LclScaling.Get());
		FbxHelper::SetDQorMat4x4FromTR(f_translation, f_rotation, m_transform);			

		if (!SetVerticeValues(a_mesh)) return false;
		if (!SetFaceValues(a_mesh)) return false;
		if (!SetBones(a_mesh, a_rootNode)) return false;				

		return true;
	}

	/// <summary>
	/// Sets the vertices and normals
	/// </summary>
	/// <param name="a_mesh">The FBX SDK mesh</param>	
	const bool GC::FbxMesh::SetVerticeValues(fbxsdk_2014_1::FbxMesh* a_mesh)
	{
		//Find out of the mesh has normals
		bool f_hasNormals = false;
		//Gets layer 0 normals so if the model has more normal layers it's going to not work
		const FbxGeometryElementNormal* f_normalElement = a_mesh->GetElementNormal(0);
		//Check some stuff! :p (If the normal is on a vertex & direct)
		if (f_normalElement->GetMappingMode() == FbxGeometryElement::eByControlPoint)
		{
			if (f_normalElement->GetReferenceMode() == FbxGeometryElement::eDirect)
			{
				f_hasNormals = true;
			}
		}
		//Get all the vertice points
		const fbxsdk_2014_1::FbxVector4* f_vertices = a_mesh->GetControlPoints();
		//Loop through all vertices
		for (int i = 0; i < m_verticeCount; ++i)
		{			
			//x
			m_vertices.push_back( f_vertices[i][0]);
			//y
			m_vertices.push_back( f_vertices[i][1]);
			//z
			m_vertices.push_back( f_vertices[i][2]);
			//If the vertex has normals
			if (f_hasNormals)
			{
				fbxsdk_2014_1::FbxVector4 f_normal = f_normalElement->GetDirectArray().GetAt(i);
				//xyz normals
				m_normals.push_back(f_normal[0]);
				m_normals.push_back(f_normal[1]);
				m_normals.push_back(f_normal[2]);
			}
		}

		return true;
	}
	/// <summary>
	/// Sets the mesh face values
	/// </summary>
	/// <param name="a_mesh">The FBX SDK Mesh</param>	
	const bool GC::FbxMesh::SetFaceValues(fbxsdk_2014_1::FbxMesh* a_mesh)
	{
		//What m_verticeIndex[ index ] 
		//It's i * 3 + j 
		
		//Loop through each face/polygon
		for (int i = 0; i < m_facesCount; ++i)
		{
			//Loop through every vertex per face/polygon, which is 3 since it's triangulated
			for (int j = 0; j < m_verticePerFace; ++j)
			{
				const int f_vertexIndex = a_mesh->GetPolygonVertex(i, j);
				
				m_verticeIndex.push_back(f_vertexIndex);
				//UV coordinates
				for (int l = 0; l < m_uvChannelsCount; ++l)
				{
					const FbxGeometryElementUV* f_elementUV = a_mesh->GetElementUV(l);

					const FbxVector2 f_UVCoords = GetUVCoords(a_mesh, f_elementUV, i, j, f_vertexIndex);
					//Function returns x:-1 y:-1 if it can't fetch coordinates
					//Since co-ordinates range from 0->1,  -1 would be error
					if (f_UVCoords[0] != -1 && f_UVCoords[1] != -1)
					{
						//face * verticesPerFace * 2 coords / vertex 
						//+ what vertex is on * 2 coords / vertex					
						m_uvCoordinates[l].push_back(f_UVCoords[0]);
						m_uvCoordinates[l].push_back( f_UVCoords[1]);
					}
					else
					{
						ConverterApp::LogMessage(L"Found bad UV coordinates");
						return false;
					}
				}				
			}
		}

		return true;
	}
	/// <summary>
	/// Create bone object(s), if deformerCount is more than 1 more bones will be created at once
	/// </summary>
	/// <param name="a_mesh">The a_mesh.</param>
	/// <param name="a_rootNode">The a_root node.</param>	
	const bool GC::FbxMesh::SetBones(fbxsdk_2014_1::FbxMesh* a_mesh, const FbxNode* a_rootNode)
	{
		//Gets the amount of deformers affecting the model
		int f_deformerCount = a_mesh->GetDeformerCount(FbxDeformer::eSkin);

		for (int i = 0; i < f_deformerCount; ++i)
		{
			FbxSkin* f_skinDeformer = (FbxSkin*)a_mesh->GetDeformer(i, FbxDeformer::eSkin);
			int f_clusterCount = f_skinDeformer->GetClusterCount();
			
			for (int j = 0; j < f_clusterCount; ++j)
			{
				FbxCluster* f_cluster = f_skinDeformer->GetCluster(j);
				//TODO: Find out why this is here, I've forgotten!
				//Probably because GetLink() is the skeleton node
				if (!f_cluster->GetLink())
				{
					continue;
				}

				//Add Bone
				FbxBonePtr f_bone = std::make_shared<FbxBone>(f_cluster, f_cluster->GetLink(), a_mesh, a_rootNode);

				m_bones.push_back(f_bone);
			}
		}

		return true;
	}

	
	/// <summary>
	/// Returns an FbxVector2 with the UV-coordinates.
	/// Returns -1, -1 if it failed to fetch coordinates	
	/// </summary>
	/// <param name="a_mesh">The FBX SDK Mesh</param>
	/// <param name="a_elementUV">Some FBX SDK geometr to get UV coords</param>
	/// <param name="i">The face/polygon index</param>
	/// <param name="j">What control point</param>
	/// <param name="a_vertexIndex">The vertex index in m_vertices</param>	
	const FbxVector2 GC::FbxMesh::GetUVCoords(fbxsdk_2014_1::FbxMesh* a_mesh, const FbxGeometryElementUV* a_elementUV, const int a_faceIndex, const int a_controlPointIndex, const int a_vertexIndex)
	{		
		switch (a_elementUV->GetMappingMode() )
		{
			
			case FbxGeometryElement::eByControlPoint:
				switch (a_elementUV->GetReferenceMode())
				{
					case FbxGeometryElement::eDirect:
						return a_elementUV->GetDirectArray().GetAt(a_vertexIndex);							
					case FbxGeometryElement::eIndexToDirect:
					{
						int id = a_elementUV->GetIndexArray().GetAt(a_vertexIndex);
						return a_elementUV->GetDirectArray().GetAt(id);					
					}
					default:
						break; // other reference modes not shown here!
				}
				break;
			case FbxGeometryElement::eByPolygonVertex:
				int f_textureUVIndex = a_mesh->GetTextureUVIndex(a_faceIndex, a_controlPointIndex);
				switch (a_elementUV->GetReferenceMode() )
				{
					case FbxGeometryElement::eDirect:
					case FbxGeometryElement::eIndexToDirect:									
						return a_elementUV->GetDirectArray().GetAt(f_textureUVIndex);							
					default:
						break; // other reference modes not shown here!
				}						
				break;			
		}	
		//Return an empty FbxVector2
		return FbxVector2(-1,-1);
	}	
}

